#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <pthread.h>
#include <sys/prctl.h>

#include "auth.h"
#include "driver.h"
#include "protocol.h"
#include "udf_crc.h"
#include "uinputux.h"

/**************************************************
*                                                 *
*   This file contents functions, which provide   *
*       interact with fingerprint scanner.        *
*                                                 *
**************************************************/

/*********************
*        UTILS       *
*********************/
static void printHex( const unsigned char* data, int length ){
	
	int i = 0;
	int k = 0;
	
	for( i = 0; i < length; i++ ){
		printf( "0x%02x ", data[i] );
		
		if( k == 7 ) printf( "    ");
		else if( k == 15 ){
			printf( "\n" );
			k = 0;
			continue;
		}
		k++;
	}
	
	if( k ) printf( "\n" );
}

static void printReceivedPacket( struct driverInstance* this ){
	printf( "*******************************    PACKET DUMP      *******************************\n" );
	printf( "   Request counter: 0x%02x, packet type 0x%02x, message body length: %i, data:\n", 
		(this->requestCounter & 0x0f), this->rcvPacketType, this->rcvPacketMessageLength );
	printHex( this->packetMessage, this->rcvPacketMessageLength );
	printf( "*******************************      DUMP END       *******************************\n" );
}

static inline unsigned char bindataCompare( const unsigned char* data1, const unsigned char* data2, int length ){
	for( int i = 0; i < length; i++ ) if( data1[i] != data2[i] )return 1;
	return 0;
}

/*********************
*       CONTROL      *
*********************/
static inline int deviceControlRead( struct driverInstance* this ){
	int res = libusb_control_transfer( this->deviceHandle, 0xc0, 4, 0, 0, this->controlStatus, 8, RQ_TIMEOUT );
	return res == 8 ? 0 : res;
}

//0x0400 reset and init
//0x0601 wake up
//0x0602 go to idle

static inline int deviceControlWrite( struct driverInstance* this, uint16_t cmd ){
	unsigned char zero[1] = { 0x00 };
	int res = libusb_control_transfer( this->deviceHandle, 0x40, 12, 0x0100, cmd, zero, 1, RQ_TIMEOUT );
	return res == 1 ? 0 : res;
}

/*********************
*       PACKET       *
*********************/
/************************************************
func packetPrepareAndSend
	Send packet to fingerptint scanner
Caller must prepare
	sendPacketType
	sendPacketMessageLength
	packetMessage(optionaly)
input
	Pointer to main driver struct
return
	negative on error
	0 on success
*************************************************/
static int packetPrepareAndSend( struct driverInstance* this ){
	
	int err;
	int needTransfer;
	int alreadyTransferred;
	int transferred;
	int bulkLength;
	uint16_t crc;
	
	//prefix
	memcpy( this->_packetData, "Ciao", 4 );
	
	//type
	this->_packetData[4] = this->sendPacketType;
	
	//<COUNTER> + length MSB
	this->_packetData[5] = this->sendPacketType ? 0 : ( this->requestCounter << 4 ) | ( (this->sendPacketMessageLength >> 8) & 0x0f );
	
	//length LSB
	this->_packetData[6] = ( this->sendPacketMessageLength & 0xff );
	
	
	//if( this->packetDataLength ){}
	
	//CRC
	crc = udf_crc ( (this->_packetData+4), (this->sendPacketMessageLength+3) );
	this->_packetData[ (this->sendPacketMessageLength+7) ] = ( crc & 0xff );
	this->_packetData[ (this->sendPacketMessageLength+8) ] = ( crc >> 8 );
	
	needTransfer = this->sendPacketMessageLength + 9;
	alreadyTransferred = 0;
	
	do{
		bulkLength = needTransfer - alreadyTransferred;
		if( bulkLength > 64 ) bulkLength = 64;
		
		err = libusb_bulk_transfer( this->deviceHandle, BULK_EP_OUT, &this->_packetData[alreadyTransferred], bulkLength, &transferred, RQ_TIMEOUT );
		if( err ){
			printf( "bulk send err: %i\n", err );
			return err;
		}
		
		if( transferred != bulkLength ){
			printf( "bulk send  len err\n" );
			return -1;
		}
		alreadyTransferred += transferred;
		
	}while( needTransfer > alreadyTransferred );
	
	return 0;
}

static int packetReceiveAndParse( struct driverInstance* this ){
	
	char stuckDetect;
	int err;
	int transferred;
	
	int needReceive;
	int alreadyReceived;
	int bulkLength;
	
	uint16_t crc;
	
	stuckDetect = 0;
	
l_receiveNext:
	err = libusb_bulk_transfer( this->deviceHandle, BULK_EP_IN, this->_packetData, 64, &transferred, RQ_TIMEOUT );
	if( err ){
		printf( "bulk rcv err1: %i\n", err );
		return err;
	}
	
	//minimum possible length is 9: ciao(4) + header(3) + crc(2)
	if( transferred < 9 ){
		printf( "received data too short: %i\n", transferred );
		return -1;
	}
	
	//prefix check
	if( strncmp( "Ciao", (const char*)this->_packetData, 4 )   ){
		printf( "Ciao prefix mismatch\n" );
		return -2;
	}
	
	//parse header
	this->rcvPacketType          =    this->_packetData[4];
	this->rcvPacketMessageLength = ( (this->_packetData[5] & 0x0f) << 8 ) | this->_packetData[6] ;
	
	//If the packet size exceeds 64 bytes, then it is split into several reception sessions.
	//the code below checks the length and gets the rest of the packet as needed
	//9 means prefix ciao(4) + header(3) + crc(2)
	if( (this->rcvPacketMessageLength+9) > 64 ){
		alreadyReceived = transferred;
		needReceive = this->rcvPacketMessageLength + 9;
		
		do{
			bulkLength = needReceive - alreadyReceived;
			if( bulkLength > 64 ) bulkLength = 64;
			
			if( (alreadyReceived + bulkLength) > BUFLEN_PACKETDATA ){
				printf( "ERROR: Receiving packet too big.\n");
				return -11;
			}
			
			err = libusb_bulk_transfer( this->deviceHandle, BULK_EP_IN, &this->_packetData[alreadyReceived], bulkLength, &transferred, RQ_TIMEOUT );
			
			if( err ){
				printf( "bulk rcv err2: %i\n", err );
				return err;
			}
			
			if( bulkLength != transferred ){
				printf( "bulk rcv err: bulk length != received\n" );
				return -1;
			}
			
			alreadyReceived += transferred;
			
		}while( alreadyReceived < needReceive );
		
		if( alreadyReceived > needReceive ){
			printf( "rcv err: alreadyReceived(%i) > needReceive(%i)\n", alreadyReceived, needReceive );
			return -3;
		}
	}
	
	//CRC check
	crc = udf_crc( &this->_packetData[4], (this->rcvPacketMessageLength+3) );
	if( this->_packetData[ (this->rcvPacketMessageLength+7) ] != ( crc & 0xff )
	 || this->_packetData[ (this->rcvPacketMessageLength+8) ] != ( crc >> 8 ) ){
		printf( "rcv packet err: crc mismatch\n" );
		return -4;
	}
	
	//answer the type 08 
	if( this->rcvPacketType == 0x08 && !this->rcvPacketMessageLength ){
		
		if( stuckDetect > 10 ){
			//TODO: scanner reset on stuck
			printf( "WARNING: Fingerprint scanner seems has been hung up!\n" );
			return -5;
		}
		
		this->sendPacketType = 0x09;
		this->sendPacketMessageLength = 0;
		err = packetPrepareAndSend( this );
		if( err ){
			printf( "rcv packet 08 answer send err: %i\n", err );
			return err;
		}
		
		stuckDetect++;
		goto l_receiveNext;
	}
	
	//Check counter if it is not a request from the device
	if( !this->rcvPacketType ){
		if( ( this->_packetData[5] >> 4 ) == (this->requestCounter & 0x0f) ){
			this->requestCounter++;
		}else{
			printf( "Reseived packet counter mismatch.\n" );
			return -66;
		}
	}
	
	return 0;
}

/**********************
*     PROCESSING      *
**********************/

static int packetSequenceMapProcessor( struct driverInstance* this, const struct packet_sequence_map* sequence, const char* mapname ){
	
	int err;
	
	printf( "Processing packet sequence map [%s]...\n", mapname );
	
	for( int i = 0; sequence[i].action; i++ ){
		
		if( sequence[i].action & ACTION_READ ){//read packet
			err = packetReceiveAndParse( this );
			if( err ){
				printf( "Receive packet error: %i, sequence: %i\n", err, i );
				return err;
			}
			
			if( this->rcvPacketType != sequence[i].type ) err |= 1;
			if( this->rcvPacketMessageLength != sequence[i].messageLength ) err |= ( 1<< 1 );
			if( bindataCompare( this->packetMessage, sequence[i].data, this->rcvPacketMessageLength ) ) err |= ( 1 << 2 );
			
			if( err ){
				printf( "\nPacket signature mismatch. flags: [%c%c%c], sequence: %i\n", 
					err & 1 ? 't' : '.', //type mismatch
					err & (1<<1) ? 'l' : '.', //length mismatch
					err & (1<<2) ? 'd' : '.', //data mismatch
					i
				);
				printReceivedPacket( this );
				if( sequence[i].action & ACT_ERR_IGN ){
					printf( "This mismatch ignored\n" );
					err = 0;
				} else return err;
			}
			
		} else {//send packet
			this->sendPacketType = sequence[i].type;
			this->sendPacketMessageLength = sequence[i].messageLength;
			if( this->sendPacketMessageLength ) memcpy( this->packetMessage, sequence[i].data, this->sendPacketMessageLength );
			err = packetPrepareAndSend( this );
			if( err ){
				printf( "Send packet error: %i, sequence: %i\n", err, i );
				return err;
			}
		}
	}//for()
	
	printf( "Packet sequence map [%s] processing done(%i).\n", mapname, err );
	return 0;
}

/****************************************************************
func commandExecute
	send command to scanner using packet type 0
input
	this - pointer to driverInstance
	cmd - pointer to message data
	cmdlen - length of message
	action - ACTION_READ if need receive and parse packet, else 0 
return
	0 on success
	not 0 on error
*****************************************************************/
static int commandExecute( struct driverInstance* this, const unsigned char* cmd, int cmdLen, unsigned char action ){
	
	int err;
	
	this->sendPacketType = 0;
	this->sendPacketMessageLength = cmdLen;
	memcpy( this->packetMessage, cmd, cmdLen );
	err = packetPrepareAndSend( this );
	if( err ){
		printf( "Failed send command to execute. code %i\n", err );
		return err;
	}
	
	if( action & ACTION_READ ){
		
		err = packetReceiveAndParse( this );
		if( err ){
			printf( "Failed receive command execute result. code: %i\n", err );
			return err;
		}
	}
	
	return 0;
}

/**********************
*       MESSAGE       *
**********************/

static int messageReceiveRestData( struct driverInstance* this ){
	
	int err;
	unsigned char confirm[1] = { 0x30 };
	
	do{
		err = commandExecute( this, confirm, 1, ACTION_READ );
		if( err ){
			printf( "(%s) Command execute error: %i\n", __FUNCTION__, err );
			return err;
		}
		
		if( this->rcvPacketType ||
			!this->rcvPacketMessageLength || 
			(this->packetMessage[0] != 0x24 && this->packetMessage[0] != 0x20) 
			){
			printf( "(%s) Wrong packet\n", __FUNCTION__ );
			printReceivedPacket( this );
			return -EPROTO;
		}
		
		if( (this->dataLength + this->rcvPacketMessageLength - 1) > BUFLEN_DATA ){
			printf( "Data too big (%i > %i)\n", (this->dataLength + this->rcvPacketMessageLength - 1), BUFLEN_DATA );
			return -EMSGSIZE;
		}
		
		memcpy( &this->data[this->dataLength], &this->packetMessage[1], (this->rcvPacketMessageLength - 1) );
		this->dataLength += this->rcvPacketMessageLength -1;
	}while( this->packetMessage[0] == 0x24 );
	
	return 0;
}

/**************************
*   FLASH MEMORY ACCESS   *
**************************/

static int flashWriteRegion( struct driverInstance* this, unsigned char region ){
	
	int err;
	
	int dataRemain;
	int dataSending;
	int dataSent;
	
	//write request template      flag   lsb   msb                           reg                     lsb   msb
	unsigned char request[15] = { 0x28, 0x00, 0x00, 0x00, 0x00,  0x01, 0x04, 0xfc, 0x58, 0xc4, 0x63, 0x00, 0x00, 0x00, 0x00  };
	const unsigned char response[7] = { 0x28, 0x04, 0x00, 0x00, 0x00, 0x01, 0x14 };
	
	if( region != 0xfb && region != 0xfc ){
		printf( "!!!ATTEMPT TO WRITE WRONG MEMORY REGION 0x%02x!!!\n", region );
		return -EINVAL;
	}
	
	dataSent = 0;
	dataRemain = this->dataLength;
	
	//message and command header
	request[12] = ( this->dataLength >> 8 ) & 0xff;//msb
	request[11] = this->dataLength & 0xff;//lsb
	request[7] = region;
	request[2] = ( (this->dataLength+12) >> 8 ) & 0xff;//msb
	request[1] = (this->dataLength+12) & 0xff;//lsb
	
	//decide size of sending data
	if( dataRemain > 2028 ){
		request[0] = 0x2c;
		dataSending = 2028;//2043 - 15
	} else {
		request[0] = 0x28;
		dataSending = this->dataLength;
	}
	
	//copy data
	this->sendPacketMessageLength = dataSending + 15;
	memcpy( this->packetMessage, request, 15 );
	if( dataSending ) memcpy( &this->packetMessage[15], this->data, dataSending );
	
l_sendData:
	
	dataRemain -= dataSending;
	
	this->sendPacketType = 0x00;
	err = packetPrepareAndSend( this );
	if( !err ) err = packetReceiveAndParse( this );
	
	if( err ){
		printf( "Memory write error: %i\n", err );
		return err;
	}
	
	//if all data sent
	if( !dataRemain ){
		
		//check the answer
		if( this->rcvPacketType || this->rcvPacketMessageLength != 7 || bindataCompare(this->packetMessage, response, 7) ){
			printf( "Memory write error: all data sent, but get wrong packet\n" );
			printReceivedPacket( this );
			return -EPROTO;
		}
		return 0;
	}
	
	//if there is data left to send
	if( this->rcvPacketType || this->rcvPacketMessageLength != 1 || this->packetMessage[0] != 0x30 ){
		printf( "Memory write error: data left, but get wrong packet\n" );
		printReceivedPacket( this );
		return -EPROTO;
	} 
	
	dataSent += dataSending;
	
	//decide size of sending data
	if( dataRemain > 2042 ){
		this->packetMessage[0] = 0x24;
		dataSending = 2042;
	} else {
		this->packetMessage[0] = 0x20;
		dataSending = dataRemain;
	}
	
	//copy next data
	this->sendPacketMessageLength = dataSending + 1;
	memcpy( &this->packetMessage[1], &this->data[dataSent], dataSending );
	
	goto l_sendData;
}

static int flashReadRegion( struct driverInstance* this, unsigned char region ){
	
	int err;
	int dataLength;
	
	//request template                                                      0xfc
	unsigned char request[11] = { 0x28, 0x08, 0x00, 0x00, 0x00, 0x02, 0x04, 0xfb, 0x58, 0xc4, 0x63 };
	
	if( region != 0xfb && region != 0xfc ){
		printf( "!!!ATTEMPT TO READ WRONG MEMORY REGION: 0x%02X!!!\n", region );
		return -EINVAL;
	}
	
	this->dataLength = 0;
	request[7] = region;
	err = commandExecute( this, request, 11, ACTION_READ );
	if( err ){
		printf( "Error executing command to read memory: %i\n", err );
		return err;
	}
	
	if( this->rcvPacketType != 0 ||
	    this->rcvPacketMessageLength < 7 || 
	    bindataCompare(&this->packetMessage[3], &request[3], 3) || 
	    this->packetMessage[6] != 0x14 
	    ){
		printf( "Error reading memory: wrong packet.\n");
		printReceivedPacket( this );
		return -EPROTO;
	}
	
	dataLength = this->packetMessage[7] | (this->packetMessage[8] << 8);
	memcpy( this->data, &this->packetMessage[11], (this->rcvPacketMessageLength - 11) );
	this->dataLength = this->rcvPacketMessageLength - 11;
	
	if( this->packetMessage[0] == 0x2c ){
		err = messageReceiveRestData( this );
		if( err ){
			printf( "Memory read error: messageReceiveRestData failed: %i\n", err );
			return err;
		}
	}
	
	if( this->dataLength != dataLength ){
		printf( "ERROR: memory read length mismatch %i != %i.\n", dataLength, this->dataLength );
		return -EPROTO;
	}
	
	return 0;
}



/*********************
*        RAM         *
*********************/
static int ramWrite( struct driverInstance* this, unsigned char flag ){
	
	int err;
	
	//set flag
	this->ramData[94] = flag;
	
	//prepare data
	this->sendPacketType = 0x00;
	this->sendPacketMessageLength = 111;
	memcpy( this->packetMessage, ramWriteRequest, 15 );
	memcpy( &this->packetMessage[15], this->ramData, BUFLEN_RAMDATA );
	
	//send data
	err = packetPrepareAndSend( this );
	if( !err ) err = packetReceiveAndParse( this );
	if( err ){
		printf( "Ram write error: %i\n", err );
		return err;
	}
	
	if( this->rcvPacketMessageLength != 7 || bindataCompare(ramWriteResponse, this->packetMessage, 7) ){
		printf( "Ram write error: got unexpeced packet.\n" );
		printReceivedPacket( this );
		return -EPROTO;
	}
	
	return err;
}

static int ramRead( struct driverInstance* this ){
	
	int err;
	
	err = commandExecute( this, ramReadRequest, 11, ACTION_READ );
	if( err ){
		printf( "Ram read: command execution error: %i\n", err );
		return err;
	}
	
	if( this->rcvPacketMessageLength != 107 || bindataCompare(ramReadResponse, this->packetMessage, 11) ){
		printf( "Ram read error: got unexpecet packet.\n" );
		printReceivedPacket( this );
		return -EPROTO;
	}
	
	memcpy( this->ramData, &this->packetMessage[11], BUFLEN_RAMDATA );
	
	return 0;
}

/*********************
*     OPERATIONS     *
*********************/

static int deviceInit( struct driverInstance* this ){
	
	int err;
	
	//write control to init and check it
	err = deviceControlWrite( this, CONTROL_INIT );
	if( !err ) err = deviceControlRead( this );
	if( !err ) err = bindataCompare( &this->controlStatus[4], state_40_a8_00_30, 4 );
	printf( "[init] Control %s.\n", err ? "error" : "success" );
	if( err ) return err;
	
	//command sequence to init device
	err = packetSequenceMapProcessor( this, init_map, "device init" );
	if( err ) return err;
	
	//ram config
	err = ramRead( this );
	if( !err ) err = ramWrite( this, RAMFLAG_DEFAULT );
	printf( "[init] Ram config %s.\n", err ? "error" : "success" );
	
	return err;
}

static int touchpadMode( struct driverInstance* this ){
	
	int err;
	int ufd;//uinput file descriptor
	
	void ( *uinputEventHandler )( int, short* );
	
	//select uinput event handler
	if(      this->touchpadMode == MODE_TOUCHPAD_MOUSE  ) uinputEventHandler = uinputEventMouse;
	else if( this->touchpadMode == MODE_TOUCHPAD_WHEELS ) uinputEventHandler = uinputEventWheels;
	else if( this->touchpadMode == MODE_TOUCHPAD_ARROWS ) uinputEventHandler = uinputEventArrows;
	else{
		printf( "[touchpad] unknown touchpad mode: 0x%02x\n", this->touchpadMode );
		return -EINVAL;
	}
	
	//open uinput device
	ufd = uinputOpen();
	if( ufd < 0 ){
		printf( "[touchpad] WARNING: Cannot open uinput.\n" );
		printf( "[touchpad] Going into idle mode.\n" );
		this->modeSwitcher = MODE_IDLE;
		return 0;
	}
	
	//switch scanner into touchpad mode
	err = commandExecute( this, touchpad_mode_request, 11, 0 );
	
l_receiveNextPacket:
	if( !err ) err = packetReceiveAndParse( this );
	if( err ){
		printf( "[touchpad] Communicate error: %i\n", err );
		goto l_modeExit;
	}
	
	//if touchpad protocol packet
	if( this->rcvPacketType == 0x0b && this->rcvPacketMessageLength == 6 ) {
		
		uinputEventHandler( ufd, (short*)this->packetMessage );
		
		this->sendPacketType = 0x0b;
		this->sendPacketMessageLength = 1;
		this->packetMessage[0] = 0x01;
		
		//change mode or uinput event handler
		if( this->modeSwitcher != MODE_TOUCHPAD ){
			if(      this->modeSwitcher == MODE_TOUCHPAD_MOUSE  ) uinputEventHandler = uinputEventMouse;
			else if( this->modeSwitcher == MODE_TOUCHPAD_WHEELS ) uinputEventHandler = uinputEventWheels;
			else if( this->modeSwitcher == MODE_TOUCHPAD_ARROWS ) uinputEventHandler = uinputEventArrows;
			else{
				this->packetMessage[0] = 0x00;//set continue flag to false for exit touchpad mode
				goto l_sendNextPacket;
			}
			
			this->touchpadMode = this->modeSwitcher;
			this->modeSwitcher = MODE_TOUCHPAD;
			sem_post( &this->semaphoreIpc );//notify ipcd about touchpad mode change
		}
		
l_sendNextPacket:
		err = packetPrepareAndSend( this );
		goto l_receiveNextPacket;
	}
	
	//if not touchpad response, then protocol error
	if( this->rcvPacketMessageLength != 7 || bindataCompare(touchpad_mode_response, this->packetMessage, 7) ) {
		printf( "[touchpad] Received wrong packet\n" );
		printReceivedPacket( this );
		err = -EPROTO;
		goto l_modeExit;
	}
	
	err = 0;
	sem_post( &this->semaphoreIpc );//notify ipcd about mode exit
	
l_modeExit:
	uinputClose( ufd );
	return err;
}

/*
//I don't know what's going on here. The Windows driver does 
//this after capturing an image from the scanner, but not always.
static int uncnownAction1( struct driverInstance* this ){
	
	int err;
	
	err = commandExecute( this, unknown_action1_request, 15, ACTION_READ );
	if( err ){
		printf( "UA1) request error: %i\n", err );
		return err;
	}
	
	if(      !bindataCompare( unknown_action1_success, this->packetMessage, 7 ) && this->rcvPacketMessageLength == 7 ) printf( "UA1) success\n" );
	else if( !bindataCompare( unknown_action1_failure, this->packetMessage, 7 ) && this->rcvPacketMessageLength == 7 ) printf( "UA1) fail\n" );
	else{
		printf( "UA1) received unknown packet\n" );
		printReceivedPacket( this );
		return -EPROTO;
	}
	
	return 0;
}
*/

//I think it has something to do with image quality. When I very quickly run 
//my finger over the scanner, values below 100% often appear here.
static int requestPictureQuality( struct driverInstance* this ){
	
	int err;
	err = commandExecute( this, image_quality_request, 7, ACTION_READ );
	if( err ){
		printf( "Image quality request error: %i\n", err );
		return err;
	}
	
	if(
	    this->rcvPacketMessageLength == 11 &&
	    !bindataCompare( image_quality_responce, this->packetMessage, 7 ) &&
	    !bindataCompare( &image_quality_responce[8], &this->packetMessage[8], 3 )
	    ){
		//0x28 0x08 0x00 0x00 0x00 0x15 0x12 0x00 0x00 0x00 0x00
		//                                   ----
		//                                 this byte
		this->scanQuality = this->packetMessage[7];
		return 0;
	}
	
	printf( "wrong [image quality] paket\n" );
	printReceivedPacket( this );
	return -EPROTO;
}

/*
static void pictureSaveFile( struct driverInstance* this ){
	
	FILE* imgFile;
	imgFile = fopen( "fimg", "w" );
	if( imgFile ){
		fwrite( "P5 144 270 255\n", 1, 15, imgFile );
		fwrite( this->data, 1, this->dataLength, imgFile );
		fclose( imgFile );
	}
}
*/

/**************
scanResuilt
	 0 - all ok
	-EIO - on driver error
	-ETIMEDOUT - on scanning timeout
	-ECANCELED - on cancel scanning
	
	 0x07 - User slid their finger too far to the left
	 0x08 - User slid their finger too far to the right
	 0x0b - The quality of the fingerprint is poor
	 0x0f - User swiped the scanner incorrectly
	 0x1c - User swiped the scanner too quickly
	 0x1d - The finger slid strongly horizontally
	 0x1e - User swiped the scanner too short
**************/
static int fingerScanPolling( struct driverInstance* this ){
	
	int err;
	
	unsigned char scan_request[14] = { 0x28, 0x0b, 0x00, 0x00, 0x00, 0x0e, 0x02, 0xc0, 0xd4, 0x01, 0x00, 0x02, 0x01, 0x00 };
	unsigned char scan_poll[8] =     { 0x28, 0x05, 0x00, 0x00, 0x00, 0x00, 0x30, 0x01 };//                     ----
	//                                                                           ----
	
l_scanAgain:
	
	scan_poll[7] = 0x01;//cancel flag
	//scan_request[12] = 0x01;//fail detection
	
	err = commandExecute( this, scan_request, 14, ACTION_READ );
	
l_poll:
	if( err ){
		printf( "[scan] finger scan request err: %i\n", err );
		goto l_error;
	}
	
	//poll response
	if( this->rcvPacketMessageLength == 20 &&
		!bindataCompare(  this->packetMessage,      _finger_scan_poll_response,    11 ) &&
		!bindataCompare( &this->packetMessage[12], &_finger_scan_poll_response[12], 8 )
		){
		
		switch( this->packetMessage[11] ){
			case 0x00: //get ready, next response will be with picture
			case 0x0c: //not ready
				break;
				
			case 0x07: //Finger shifted to the left
			case 0x08: //Finger shifted to the right
			case 0x0b: //Bad quality
			case 0x0f: //Wrong swipe
			case 0x1c: //Swiped too fast
			case 0x1d: //Finger shifted
			case 0x1e: //Swiped too short
				this->scanResult = this->packetMessage[11];
				this->driverState = STATE_WAIT_IPC;
				sem_post( &this->semaphoreIpc );
				sem_wait( &this->semaphoreScan );
				this->driverState = STATE_RUNNING;
				break;
				
			default:
				printf( "[scan] Warning: unknown poll code 0x%02x\n", this->packetMessage[11] );
		}
		
		if( this->modeSwitcher != MODE_SCANNING ) scan_poll[7] = 0x00;
		err = commandExecute( this, scan_poll, 8, ACTION_READ );
		goto l_poll;
	}
	
	if( this->rcvPacketMessageLength == 7 ){
		if( !bindataCompare(this->packetMessage, finger_scan_response_canceled, 7) ){
			this->scanResult = -ECANCELED;
			goto l_scanFinish;
		}
		
		if( !bindataCompare(this->packetMessage, finger_scan_response_timeout, 7) ){
			this->scanResult = -ETIMEDOUT;
			goto l_scanFinish;
		 }
	}
	
	//answer with picture?
	if( this->rcvPacketMessageLength == 2043 && !bindataCompare(this->packetMessage, finger_scan_response_picture, 11) ){
		
		//2043 - 11 = 2032
		memcpy( this->data, &this->packetMessage[11], 2032 ); 
		this->dataLength = 2032;
		
		err = messageReceiveRestData( this );
		if( !err ) err = requestPictureQuality( this );
		
		if( err ){
			printf( "[scan] picture receive or quality request error: %i\n", err );
			goto l_error;
		}
		
		if( this->dataLength != 38880 ){
			printf( "[scan] Error: Unexpected image size: %i.\n", this->dataLength );
			err = -EPROTO;
			goto l_error;
		}
		
		printf( "[scan] Received image quality %i%%\n", this->scanQuality );
		
		this->scanResult = 0;
		goto l_scanFinish;
	}
	
	printf( "[scan] Unknown packet received:\n" );
	printReceivedPacket( this );
	err = -EPROTO; //unknown packet
	
l_error:
	this->scanResult = -EIO;
	this->driverState = STATE_WAIT_IPC;
	sem_post( &this->semaphoreIpc );
	sem_wait( &this->semaphoreScan );
	this->driverState = STATE_RUNNING;
	return err;
	
l_scanFinish:
	this->driverState = STATE_WAIT_IPC;
	sem_post( &this->semaphoreIpc );
	sem_wait( &this->semaphoreScan );
	this->driverState = STATE_RUNNING;
	if( this->modeSwitcher == MODE_SCANNING ){
		printf( "[scan] Scanning again\n" );
		goto l_scanAgain;
	}
	return 0;
}


static int idlingMode( struct driverInstance* this ){
	
	int err;
	struct timespec timeToWaitSemaphore;
	
l_go_to_idle:
	//send command to idle
	err = commandExecute( this, cmd_to_idle, 14, 0 );
	if( err ){
		printf( "[idle] command to idle error: %i\n", err );
		return err;
	}
	
	//set control to idle
	err = deviceControlWrite( this, CONTROL_TO_IDLE );
	if( err ){
		printf( "[idle] control to idle error: %i\n", err );
		return err;
	}
	
	//request interrupt and wait semaphore
	if( this->interruptStatus ) sem_post( &this->semaphoreInterrupt );
l_waitSemaphore:
	sem_wait( &this->semaphoreIdle );
	
	//passive auth mode switch
	if( this->passiveAuthModeSwitcher != this->passiveAuthModeIndicator ){
		this->passiveAuthModeIndicator = this->passiveAuthModeSwitcher;
		this->driverState = STATE_WAIT_IPC;
		sem_post( &this->semaphoreIpc );
		sem_wait( &this->semaphoreScan );
		this->driverState = STATE_RUNNING;
		goto l_waitSemaphore;
	}
	
	//check error in interrupt thread
	if( this->interruptStatus < 0 ){
		printf( "[idle] interrupt thread return error: %i\n", this->interruptStatus );
		return this->interruptStatus;
	}
	
	//set control to wake up
	err = deviceControlWrite( this, CONTROL_WAKE_UP );
	if( err ){
		printf( "[idle] device control wake up error: %i\n", err );
		return err;
	}
	
	//read "to idle" comand response
	err = packetReceiveAndParse( this );
	if( err ){
		printf( "[idle] read 'to idle' command response error: %i\n", err );
		return err;
	}
	
	//check received packet type
	if( this->rcvPacketType ){
		printf( "[idle] received unexpected packet:\n" );
		printReceivedPacket( this );
		return -EPROTO;
	}
	
	//maybe this is failed finger scan?
	if( this->rcvPacketMessageLength == 7 ){
		//This occurs when poorly or accidentally touching the fingerprint scanner.
		if( !bindataCompare(this->packetMessage, idle_answer_error1, 7) ) goto l_go_to_idle;
		if( !bindataCompare(this->packetMessage, idle_answer_error2, 7) ) goto l_go_to_idle;
	}
	
	//maybe this is fingerprint image?
	if( this->rcvPacketMessageLength == 2043 && !bindataCompare(this->packetMessage, idle_answer_picture, 19) ){
		
		memcpy( this->data, &this->packetMessage[19], (this->rcvPacketMessageLength - 19) );
		this->dataLength = this->rcvPacketMessageLength - 19;
		err = messageReceiveRestData( this );
		if( err ){
			printf( "[idle] receive picture error: %i\n", err );
			return err;
		}
		
		if( this->dataLength != 38880 ){
			printf( "[idle] Error: Unexpected image size: %i.\n", this->dataLength );
			return -EPROTO;
		}
		
		err = requestPictureQuality( this );
		if( err ){
			printf( "[idle] PictureQuality request error: %i\n", err );
			return err;
		}
		
		printf( "[idle] Received image quality %i%%\n", this->scanQuality );
		
		authFingerTrigger( this );
		//pictureSaveFile( this );
		goto l_go_to_idle;
	}
	
	//if this is not a correct awakening, then something is wrong
	if( this->rcvPacketMessageLength != 15 || bindataCompare(this->packetMessage, idle_answer, 15) ){
		printf( "[idle] received unexpected packet:\n" );
		printReceivedPacket( this );
		return -EPROTO;
	}
	
	//if current mode is idle, then go to idle
	if( this->modeSwitcher == MODE_IDLE ){
		printf( "[idle] WARNING: Wake up without action.\n" );
		printf( "[idle] Last data in control status: " );
		printHex( this->controlStatus, 4 );
		printf( "[idle] Going to idle.\n" );
		goto l_go_to_idle;
	}
	
	//get current time and add 5 sec
	err = clock_gettime( CLOCK_REALTIME, &timeToWaitSemaphore );
	if( err ){
		perror( "[idle] clock" );
		return err;
	}
	timeToWaitSemaphore.tv_sec += 5;
	
	//waiting for an interruption that come in the process of awakening
	err = sem_timedwait( &this->semaphoreIdle, &timeToWaitSemaphore );
	if( err && errno == ETIMEDOUT ){
		printf( "[idle] WARNING: After wake up interrupt not got.\n" );
		printf( "[idle] Last data in control register: ");
		printHex( this->controlStatus, 4 );
	}
	
	sem_post( &this->semaphoreIpc ); //notifi ipcd about mode exiting
	return 0;
}

/****************************
*     INTERRUPT HANDLER     *
****************************/

//this function works in a separate thread
static void* deviceInterruptHandler( void* arg ){
	
	struct driverInstance* this = arg;
	int err;
	int transferred;
	
	prctl( PR_SET_NAME, "IRQ", 0, 0, 0 );
	
l_waitSemaphore:
	this->interruptStatus = 1;
	sem_wait( &this->semaphoreInterrupt );
	this->interruptStatus = 0;
	
l_waitInterrupt:
	err = libusb_interrupt_transfer( this->deviceHandle, INT_EP_IN, this->controlStatus, 4, &transferred, 0 );
	if( err ){
		printf( "[interrupt] ERROR: Transfer return code %i\n", err );
		this->interruptStatus = err;
		goto l_error;
	}
	
	if( transferred != 4 ){
		printf( "[interrupt] ERROR: Transferred != 4\n" );
		this->interruptStatus = -1;
		goto l_error;
	}
	
	//ignore these interrupts
	if( !bindataCompare(this->controlStatus, state_00_80_00_30, 4) ) goto l_waitInterrupt;
	if( !bindataCompare(this->controlStatus, state_00_80_02_30, 4) ) goto l_waitInterrupt;
	if( !bindataCompare(this->controlStatus, state_00_80_03_30, 4) ) goto l_waitInterrupt;
	if( !bindataCompare(this->controlStatus, state_00_c0_03_30, 4) ) goto l_waitInterrupt;
	if( !bindataCompare(this->controlStatus, state_38_b0_00_30, 4) ) goto l_waitInterrupt;
	
	//these interrupts come in the process of awakening
	if( !bindataCompare(this->controlStatus, state_40_a8_00_30, 4) ) goto l_sem_post;
	if( !bindataCompare(this->controlStatus, state_40_b0_00_30, 4) ) goto l_sem_post;
	
	//user touch fingerprint scanner
	if( !bindataCompare(this->controlStatus, state_40_a8_02_30, 4) ) goto l_sem_post;
	
	printf( "[interrupt] WARNING: Unknown state: " );
	printHex( this->controlStatus, 4 );
	
l_sem_post:
	sem_post( &this->semaphoreIdle );
	goto l_waitSemaphore;
	
l_error:
	sem_post( &this->semaphoreIdle );
	return NULL;
}

/***************************************************************
semaphoreIpc up rules
	notification about entering mode sends by driverModeSwitcher
	notification about exiting mode sends by a mode handler
***************************************************************/
static int driverModeSwitcher( struct driverInstance* this ){
	
	int err;
	
	//just in case
	this->modeIndicator = MODE_IDLE;
	this->modeSwitcher  = MODE_IDLE;
	
	//do not semaphoreIpc up on driver starts
	goto l_startIdle;
	
l_switchMode:
	
	if( this->modeSwitcher == MODE_IDLE ){
		this->modeIndicator = MODE_IDLE;
		sem_post( &this->semaphoreIpc );//notify ipc about mode change
l_startIdle:
		printf( "[switch] Entering into idle mode...\n" );
		err = idlingMode( this );
		if( err ){
			printf( "[switch] Idling mode exited with code: %i\n", err );
			return err;
		}
		goto l_switchMode;
	}
	
	if( this->modeSwitcher == MODE_TOUCHPAD ){
		this->modeIndicator = MODE_TOUCHPAD;
		printf( "[switch] Entering into touchpad mode...\n" );
		sem_post( &this->semaphoreIpc );//notify ipc about mode change
		err = touchpadMode( this );
		if( err ){
			printf( "[switch] touchpadMode returns error: %i\n", err );
			return err;
		}
		
		goto l_switchMode;
	}
	
	if( this->modeSwitcher == MODE_SCANNING ){
		this->modeIndicator = MODE_SCANNING;
		err = ramWrite( this, RAMFLAG_SCAN );
		if( !err ){
			printf( "[switch] Entering into scanning mode...\n" );
			sem_post( &this->semaphoreIpc );//notify ipc about mode change
			err = fingerScanPolling( this );
		}
		if( !err ) err = ramWrite( this, RAMFLAG_DEFAULT );
		
		if( err ){
			printf( "[switch] fingerScanPolling error: %i\n", err );
			return err;
		}
		
		goto l_switchMode;
	}
	
	printf( "[switch] WARNING! Unknown mode switch 0x%02x.\n", this->modeSwitcher );
	return -EINVAL;
}

/********************
*       ENTRY       *
********************/

void* ipcdEntryPoint( void* arg );

int daemonEntryPoint( libusb_device_handle* deviceHandle, int launcherdSocket ){
	
	int err;
	pthread_t threadIpcd;
	pthread_t threadInterrupt;
	
	struct driverInstance this;
	
	//init some struct fields
	this.deviceHandle = deviceHandle;
	this.launcherdSocket = launcherdSocket;
	this.packetMessage = &this._packetData[7];
	this.requestCounter = 0x00;
	this.interruptStatus = 1;
	this.modeIndicator = MODE_IDLE;
	this.modeSwitcher  = MODE_IDLE;
	this.driverState   = STATE_RUNNING;
	this.passiveAuthUsername  = NULL;
	this.passiveAuthModeSwitcher  = PA_STOP;
	this.passiveAuthModeIndicator = PA_STOP;
	
	
	sem_init( &this.semaphoreIpc,       0, 0 );
	sem_init( &this.semaphoreIdle,      0, 0 );
	sem_init( &this.semaphoreScan,      0, 0 );
	sem_init( &this.semaphoreInterrupt, 0, 0 );
	
	err = deviceInit( &this );//init device
	
	if( !err ) err = pthread_create( &threadInterrupt, NULL, deviceInterruptHandler, &this );
	if( !err ) err = pthread_create( &threadIpcd,      NULL, ipcdEntryPoint,         &this );
	if( !err ) err = driverModeSwitcher( &this );//go to main loop
	
	//
	//
	//TODO: correct threads stop
	//
	//
	
	//cleanup
	sem_destroy( &this.semaphoreIpc );
	sem_destroy( &this.semaphoreIdle );
	sem_destroy( &this.semaphoreScan );
	sem_destroy( &this.semaphoreInterrupt );
	
	return err;
}













//do not notice about unused functions
void shutUp( void ){
	flashReadRegion( NULL, 0 );
	flashWriteRegion( NULL, 0 );
}
